#include "znx_http_conf.h"
#include "comm/znx_pool.h"
#include "comm/znx_module.h"
#include "comm/znx_yaml_parser.h"


znx_http_main_conf_t            *g_http_main_conf;


static znx_bool_t znx_http_main_server_core_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);

static znx_bool_t znx_http_main_proxy_core_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);

static znx_bool_t znx_http_main_modules_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err);


static void
znx_http_main_create()
{
    znx_pool_t                  *pool;
    pool = znx_pool_create_ext(ZNX_POOL_MIN_PAGE_SIZE);
    g_http_main_conf = znx_pool_calloc(pool, sizeof(znx_http_main_conf_t));
    g_http_main_conf->pool = pool;
    g_http_main_conf->modules = znx_pool_calloc(pool,
        sizeof(void *) * (size_t)http_modules_num);
}


void
znx_http_main_destroy()
{
    if (g_http_main_conf) {
        znx_pool_destroy(g_http_main_conf->pool);
    }
}


static znx_command_t znx_http_main_commands[] = {
    {
        ZNX_STRING("lookup_depth"),
        znx_command_set_int,
        offsetof(znx_http_main_conf_t, lookup_depth)
    },

    {
        ZNX_STRING("server_core_conf"),
        znx_http_main_server_core_conf_parse,
        0
    },

    {
        ZNX_STRING("proxy_core_conf"),
        znx_http_main_proxy_core_conf_parse,
        0
    },

    {
        ZNX_STRING("modules"),
        znx_http_main_modules_conf_parse,
        0
    },

    znx_null_command
};


znx_command_t znx_http_core_commands[] = {
    {
        ZNX_STRING("ignore_invalid_headers"),
        znx_command_set_bool,
        offsetof(znx_http_core_conf_t, ignore_invalid_headers)
    },

    {
        ZNX_STRING("request_header_buf_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, request_header_buf_size)
    },

    {
        ZNX_STRING("max_request_header_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, max_request_header_size)
    },

    {
        ZNX_STRING("request_body_buf_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, request_body_buf_size)
    },

    {
        ZNX_STRING("max_read_request_body_size_limit"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, max_read_request_body_size_limit)
    },

    {
        ZNX_STRING("read_request_header_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, read_request_header_timeout_ms)
    },

    {
        ZNX_STRING("read_request_body_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, read_request_body_timeout_ms)
    },

    {
        ZNX_STRING("write_response_body_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, write_response_body_timeout_ms)
    },
    
    {
        ZNX_STRING("response_header_buf_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, response_header_buf_size)
    },

    {
        ZNX_STRING("max_response_header_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, max_response_header_size)
    },

    {
        ZNX_STRING("response_body_buf_size"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, response_body_buf_size)
    },

    {
        ZNX_STRING("max_read_response_body_size_limit"),
        znx_command_set_sizet,
        offsetof(znx_http_core_conf_t, max_read_response_body_size_limit)
    },

    {
        ZNX_STRING("read_response_header_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, read_response_header_timeout_ms)
    },

    {
        ZNX_STRING("read_response_body_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, read_response_body_timeout_ms)
    },

    {
        ZNX_STRING("write_request_body_timeout_ms"),
        znx_command_set_int64,
        offsetof(znx_http_core_conf_t, write_request_body_timeout_ms)
    },

    znx_null_command
};


znx_http_core_conf_t *
znx_http_core_conf_create(znx_pool_t *pool)
{
    znx_http_core_conf_t        *http_core_conf;
    http_core_conf = znx_pool_calloc(pool, sizeof(znx_http_core_conf_t));

    http_core_conf->ignore_invalid_headers = ZNX_BOOL_UNSET;

    http_core_conf->request_header_buf_size = ZNX_SIZET_UNSET;
    http_core_conf->max_request_header_size = ZNX_SIZET_UNSET;
    http_core_conf->request_body_buf_size = ZNX_SIZET_UNSET;
    http_core_conf->max_read_request_body_size_limit = ZNX_SIZET_UNSET;
    http_core_conf->read_request_header_timeout_ms = ZNX_INT64_UNSET;
    http_core_conf->read_request_body_timeout_ms = ZNX_INT64_UNSET;
    http_core_conf->write_response_body_timeout_ms = ZNX_INT64_UNSET;

    http_core_conf->response_header_buf_size = ZNX_SIZET_UNSET;
    http_core_conf->max_response_header_size = ZNX_SIZET_UNSET;
    http_core_conf->response_body_buf_size = ZNX_SIZET_UNSET;
    http_core_conf->max_read_response_body_size_limit = ZNX_SIZET_UNSET;
    http_core_conf->read_response_header_timeout_ms = ZNX_INT64_UNSET;
    http_core_conf->read_response_body_timeout_ms = ZNX_INT64_UNSET;
    http_core_conf->write_request_body_timeout_ms = ZNX_INT64_UNSET;

    return http_core_conf;
}


znx_bool_t
znx_http_core_conf_check_and_fix(znx_http_core_conf_t *core_conf)
{
    if (core_conf->ignore_invalid_headers == ZNX_BOOL_UNSET) {
        core_conf->ignore_invalid_headers = ZNX_TRUE;
    }

    ///////////////////////////////////////////////////

    if (core_conf->request_header_buf_size == ZNX_SIZET_UNSET) {
        core_conf->request_header_buf_size = 8192;
    }

    if (core_conf->request_header_buf_size < 128) {
        core_conf->request_header_buf_size = 128;
    }

    if (core_conf->max_request_header_size == ZNX_SIZET_UNSET) {
        core_conf->max_request_header_size = 8192;
    }

    if (core_conf->max_request_header_size < 256) {
        core_conf->max_request_header_size = 256;
    }

    if (core_conf->request_body_buf_size == ZNX_SIZET_UNSET) {
        core_conf->request_body_buf_size = 32 * 1024;
    }

    if (core_conf->request_body_buf_size < 512) {
        core_conf->request_body_buf_size = 512;
    }

    if (core_conf->max_read_request_body_size_limit == ZNX_SIZET_UNSET) {
        core_conf->max_read_request_body_size_limit = 64 * 1024;
    }

    if (core_conf->max_read_request_body_size_limit < 512) {
        core_conf->max_read_request_body_size_limit = 512;
    }

    if (core_conf->read_request_header_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->read_request_header_timeout_ms = 10000;
    }

    if (core_conf->read_request_header_timeout_ms < 10) {
        core_conf->read_request_header_timeout_ms = 10;
    }

    if (core_conf->read_request_body_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->read_request_body_timeout_ms = 10000;
    }

    if (core_conf->read_request_body_timeout_ms < 10) {
        core_conf->read_request_body_timeout_ms = 10;
    }

    if (core_conf->write_response_body_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->write_response_body_timeout_ms = 10000;
    }

    if (core_conf->write_response_body_timeout_ms < 10) {
        core_conf->write_response_body_timeout_ms = 10;
    }

    //////////////////////////////////////////////////////

    if (core_conf->response_header_buf_size == ZNX_SIZET_UNSET) {
        core_conf->response_header_buf_size = 8192;
    }

    if (core_conf->response_header_buf_size < 128) {
        core_conf->response_header_buf_size = 128;
    }

    if (core_conf->max_response_header_size == ZNX_SIZET_UNSET) {
        core_conf->max_response_header_size = 8192;
    }

    if (core_conf->max_response_header_size < 256) {
        core_conf->max_response_header_size = 256;
    }

    if (core_conf->response_body_buf_size == ZNX_SIZET_UNSET) {
        core_conf->response_body_buf_size = 32 * 1024;
    }

    if (core_conf->request_body_buf_size < 512) {
        core_conf->request_body_buf_size = 512;
    }

    if (core_conf->max_read_response_body_size_limit == ZNX_SIZET_UNSET) {
        core_conf->max_read_response_body_size_limit = 64 * 1024;
    }

    if (core_conf->max_read_response_body_size_limit < 512) {
        core_conf->max_read_response_body_size_limit = 512;
    }

    if (core_conf->read_response_header_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->read_response_header_timeout_ms = 10000;
    }

    if (core_conf->read_response_header_timeout_ms < 10) {
        core_conf->read_response_header_timeout_ms = 10;
    }

    if (core_conf->read_response_body_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->read_response_body_timeout_ms = 10000;
    }

    if (core_conf->read_response_body_timeout_ms < 10) {
        core_conf->read_response_body_timeout_ms = 10;
    }

    if (core_conf->write_request_body_timeout_ms == ZNX_INT64_UNSET) {
        core_conf->write_request_body_timeout_ms = 10000;
    }

    if (core_conf->write_request_body_timeout_ms < 10) {
        core_conf->write_request_body_timeout_ms = 10;
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_http_core_conf_merger_and_fix(znx_http_core_conf_t *conf,
    znx_http_core_conf_t *pre_conf)
{
    if (conf->ignore_invalid_headers == ZNX_BOOL_UNSET) {
        conf->ignore_invalid_headers =
            pre_conf->ignore_invalid_headers;
    }

    //////////////////////////////////////////////////////

    if (conf->request_header_buf_size == ZNX_SIZET_UNSET) {
        conf->request_header_buf_size =
            pre_conf->request_header_buf_size;
    }

    if (conf->max_request_header_size == ZNX_SIZET_UNSET) {
        conf->max_request_header_size =
            pre_conf->max_request_header_size;
    }

    if (conf->request_body_buf_size == ZNX_SIZET_UNSET) {
        conf->request_body_buf_size =
            pre_conf->request_body_buf_size;
    }

    if (conf->max_read_request_body_size_limit == ZNX_SIZET_UNSET) {
        conf->max_read_request_body_size_limit =
            pre_conf->max_read_request_body_size_limit;
    }

    if (conf->read_request_header_timeout_ms == ZNX_INT64_UNSET) {
        conf->read_request_header_timeout_ms =
            pre_conf->read_request_header_timeout_ms;
    }

    if (conf->read_request_body_timeout_ms == ZNX_INT64_UNSET) {
        conf->read_request_body_timeout_ms =
            pre_conf->read_request_body_timeout_ms;
    }

    if (conf->write_response_body_timeout_ms == ZNX_INT64_UNSET) {
        conf->write_response_body_timeout_ms =
            pre_conf->write_response_body_timeout_ms;
    }

    /////////////////////////////////////////////////////

    if (conf->response_header_buf_size == ZNX_SIZET_UNSET) {
        conf->response_header_buf_size =
            pre_conf->response_header_buf_size;
    }

    if (conf->max_response_header_size == ZNX_SIZET_UNSET) {
        conf->max_response_header_size =
            pre_conf->max_response_header_size;
    }

    if (conf->response_body_buf_size == ZNX_SIZET_UNSET) {
        conf->response_body_buf_size =
            pre_conf->response_body_buf_size;
    }

    if (conf->max_read_response_body_size_limit == ZNX_SIZET_UNSET) {
        conf->max_read_response_body_size_limit =
            pre_conf->max_read_response_body_size_limit;
    }

    if (conf->read_response_header_timeout_ms == ZNX_INT64_UNSET) {
        conf->read_response_header_timeout_ms =
            pre_conf->read_response_header_timeout_ms;
    }

    if (conf->read_response_body_timeout_ms == ZNX_INT64_UNSET) {
        conf->read_response_body_timeout_ms =
            pre_conf->read_response_body_timeout_ms;
    }

    if (conf->write_request_body_timeout_ms == ZNX_INT64_UNSET) {
        conf->write_request_body_timeout_ms =
            pre_conf->write_request_body_timeout_ms;
    }

    return znx_http_core_conf_check_and_fix(conf);
}


static znx_bool_t
znx_http_main_server_core_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    if (node->type != YAML_MAPPING_NODE) {
        ZNX_STR_SET(err, "server_core_conf type is not mapping node");
        return ZNX_FALSE;
    }

    // create server_core_conf
    g_http_main_conf->server_core_conf = znx_http_core_conf_create(pool);
    return znx_yaml_map_node_parser(
        g_http_main_conf->server_core_conf, g_http_main_conf->pool,
        doc, node, znx_http_core_commands, err);
}


static znx_bool_t
znx_http_main_proxy_core_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    if (node->type != YAML_MAPPING_NODE) {
        ZNX_STR_SET(err, "proxy_core_conf type is not mapping node");
        return ZNX_FALSE;
    }

    // create proxy_core_conf
    g_http_main_conf->proxy_core_conf = znx_http_core_conf_create(pool);
    return znx_yaml_map_node_parser(
        g_http_main_conf->proxy_core_conf, g_http_main_conf->pool,
        doc, node, znx_http_core_commands, err);
}


static znx_bool_t
znx_http_main_modules_conf_parse(void *conf,
    znx_pool_t *pool, znx_command_t *command,
    yaml_document_t *doc, yaml_node_t *node, znx_str_t *err)
{
    if (node->type != YAML_MAPPING_NODE) {
        ZNX_STR_SET(err, "http_main modules type is not mapping node");
        return ZNX_FALSE;
    }

    yaml_node_pair_t            *pair;
    yaml_node_t                 *key;
    yaml_node_t                 *value;
    size_t                      err_buf_len = command->name.len + 128;

    for (pair = node->data.mapping.pairs.start;
        pair < node->data.mapping.pairs.top;
        pair++)
    {
        key = yaml_document_get_node(doc, pair->key);
        value = yaml_document_get_node(doc, pair->value);

        if (key->type != YAML_SCALAR_NODE) {
            err->data = znx_pool_malloc(pool, err_buf_len);
            err->len = (size_t)(znx_snprintf(err->data, err_buf_len,
                "command %v element key node type must is YAML_SCALAR_NODE",
                &command->name) - err->data);
            return ZNX_FALSE;
        }

        for (int i = 0; i < http_modules_num; i++) {
            if (key->data.scalar.length == http_modules[i]->name.len &&
                znx_strncmp(key->data.scalar.value,
                    http_modules[i]->name.data,
                    http_modules[i]->name.len) == 0)
            {
                if (http_modules[i]->main_conf_parse) {
                    if (!http_modules[i]->main_conf_parse(g_http_main_conf,
                        pool, doc, value, err))
                    {
                        return ZNX_FALSE;
                    }
                }

                break;
            }
        }
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_http_main_conf_check_and_fix()
{
    if (g_http_main_conf->lookup_depth <= 0) {
        g_http_main_conf->lookup_depth = 3;
    }

    if (g_http_main_conf->server_core_conf == NULL) {
        g_http_main_conf->server_core_conf =
            znx_http_core_conf_create(g_http_main_conf->pool);
    }

    if (!znx_http_core_conf_check_and_fix(g_http_main_conf->server_core_conf)) {
        return ZNX_FALSE;
    }

    if (g_http_main_conf->proxy_core_conf == NULL) {
        g_http_main_conf->proxy_core_conf =
            znx_http_core_conf_create(g_http_main_conf->pool);
    }

    if (!znx_http_core_conf_check_and_fix(g_http_main_conf->proxy_core_conf)) {
        return ZNX_FALSE;
    }

    return ZNX_TRUE;
}


znx_bool_t
znx_http_main_conf_parse(const char *filepath)
{
    znx_http_main_create();

    if (!znx_yaml_map_conf_parser(g_http_main_conf,
        g_http_main_conf->pool, filepath,
        znx_http_main_commands, &g_http_main_conf->err))
    {
        return ZNX_FALSE;
    }

    if (!znx_http_main_conf_check_and_fix()) {
        return ZNX_FALSE;
    }

    return znx_http_module_main_conf_post_exec();
}